""" sbak.event - Event processing.

    ScrollBack has a single object that plays the role of both the "subject"
    and the "observer" in traditional observer-pattern event processing. This
    object is called an "eventuser". The terms "subject" and "observer" are
    only used in reference to what an eventuser is doing at a particular
    moment in time; when it is observing, it is called an "observer", and when
    it is emitting or posting events it is called a "subject".


    TODO: Design a mechanism for stopping event processing from within a handler. (Return a value? Raise a custom exception, ala StopIteration?)
    
    
    Important notes about class-level event handler methods:
    
    
    #1. If a method overrides a superclass's method and the superclass
        method was an event handler, the overriding method will NOT
        automatically become an event handler, and ONLY the superclass method
        will  be hooked. This means that an overriding method must be
        explicitly defined as handler.
        
    #2. If a method overrides a superclass method and both methods are
        defined as handlers, only the overriding method will be hooked.
        The overridden method must be called explicitly from within the
        overriding method, just like any other overriding method.
        
    #3. It is a bad idea to override a handler method with another handler
        method that handles a different signal than the overridden handler.
        Don't do this!
"""

import operator
import Queue

from sbak.error import Error
from sbak.base import Object
from sbak.tree import AbstractBranch, AbstractNode, INode, IBranch


def handler(signal_or_type, priority=None, desc=(), **kwdesc):
    """ Use to turn a method in an eventuser class's body to an event handler.
        
        handler(signal_or_type) -> decorator
        handler(signal_or_type, priority) -> decorator
        handler(signal_or_type, priority, desc) -> decorator
        handler(signal_or_type, priority,
            key1=val1, ..., keyN=valN) -> decorator
        handler(signal_or_type,
            key1=val1, ..., keyN=valN) -> decorator
        
        signal_or_type: The signal of an event type, or the class of the type
                        from which the signal can be acquired.
        
        priority:       Optional priority. If None or not supplied, the
                        hook created for the handler will be given a priority
                        at the time the handler is hooked.
        
        desc:           Optional event description. Must be something that can
                        be passed to the `dict` function.
        
        This function is used to indicate that a method within a class body
        should be used as an event handler for every instance of the class.
        When used in a subclass of AbstractEventUser, the method will be hooked
        when objects of the class are initialized.
        
        This function is used as a decorator function factory; that is, a
        function that manufactures decorator-compatible functions. To use the
        `handler` function correctly, use the object that it returns (a
        function) as a decorator:
        
        >>> class MyEventUser(AbstractEventUser):
        ...     @handler(FooEvent)
        ...     def handle_foo(self, hook, e):
        ...         do_some_stuff
    
        In this example, `handle_foo` will be hooked to the signal of FooEvent
        for each instance of `MyEventUser` that is intialized. It is
        effectively equivalent to coding the eventuser's __init__ method to
        hook the method to the signal of FooEvent, as in the following:
        
        >>> class MyEventUser(AbstractEventUser):
        ...     def __init__(self):
        ...         AbstractEventUser.__init__(self)
        ...         self.hook(self.handle_foo, FooEvent)
        
        You can also specify an event description using the optional argument
        `desc` and/or keyword arguments. These work the same way as with
        `AbstractEventUser.hook(self, handler, signal, desc, **kwdesc)`.
        
        Methods that get passed through the decorators returned by the
        `handler` function will be given the following attributes:
        
        is_handler:     The presence of this attribute on a method indicates
                        that the method is an event handler. The value of the
                        attribute is unimportant; only presence matters.
        
        signal:         The event signal for which the event handler
                        will listen.
        
        priority:       The priority value that will be used for the hooks
                        created from the handler.
        
        desc:           An event description. This will be a dict object. If
                        no description was given, the dict will be empty.
    """
    desc = dict(desc)
    desc.update(kwdesc)
    def decorator(h):
        try:
            h.signal = signal_or_type.signal
        except AttributeError:
            h.signal = signal_or_type
        h.priority = None
        h.desc = desc
        h.is_event_handler = True
        return h
    return decorator

class EventM(Object.__metaclass__):
    """ Used to autogenerate the `signals` attribute of Event types. """
    def __init__(cls, name, bases, dict_):
        super(EventM,cls).__init__(name, bases, dict_)
        signals = set()
        for c in cls.__mro__:
            try:
                signals.add(c.signal)
            except AttributeError:
                pass
        cls.signals = frozenset(signals)

class Event(Object):
    """ Base event object.
    
        Event(subject) -> e
        Event(subject, root) -> e
        
        subject:    Eventuser that emitted or posted the event.
        
        root:       Optional. Indicates the root node used when spidering.
        
        An event is a message that describes something that has happened. There
        are many different types of such messages; the Event type is used as
        the base for all other such types.
        
        This class defines the event data attributes that are common to all
        events.
        
        Note that you don't normally create instances of event types
        directly; instead, you pass the event type itself to the `emit`,
        `spider`, `post`, or `spiderpost` methods of eventuser objects:
        
        eventuser.emit(Event) -> e
        eventuser.post(Event) -> e
        eventuser.spider(Event) -> e
        eventuser.spider(Event, root) -> e
        eventuser.spiderpost(Event) -> e
        eventuser.spiderpost(Event, root) -> e
        
        If you want to create an event object directly by calling an event
        class (ie. the normaly way of creating an class instance), the event
        must be emitted/posted/spidered/spiderposted using methods of the
        eventhub for an eventuser, or the node from which the event will be
        spidered:
        
        >>> # Emit using the hub
        >>> e = Event(eventuser)
        >>> eventuser.eventhub.emit(e)
        
        >>> # Post using the hub
        >>> e = Event(eventuser)
        >>> eventuser.eventhub.post(e)
        
        >>> # Spider using the main tree root node
        >>> e = Event(eventuser, e.get_root())
        >>> eventuser.get_root().continue_spider(e)
        
        >>> # Spiderpost using the main tree root node and the hub
        >>> e = Event(eventuser, e.get_root())
        >>> eventuser.eventhub.post(e) # Can be used for post or spiderpost
    
        ~~~ Class Attributes ~~~
        
        signal:     An event's signal. The signal is the most basic and
                    important piece of data for an event. 
                    
                    The signal for the base event type is "default".
                    
                    This attribute must be overridden by every subclass. All
                    events of a particular type should have a signal that is
                    unique to that type, and all events of that type should
                    have the same signal.
                    
        signals:    A frozenset containing the signal of the current class as
                    well as all superclasses. This will be generated
                    automatically for each class that inherits from Event, so
                    don't create it manually.
        
        ~~~ Other Attributes ~~~
        
        NOTE:
        These attributes are read-only.
        
        subject:    The eventuser that "caused" this event to occur. Every
                    event must be "caused" by something. None is not a valid
                    value, since it intuitively means that the event ocurred
                    on its own.
                    
        sid:        ID of the subject. If the subject does not have an ID then
                    this will be None.
        
        root:       Used in asyncronous spidering. When an event is posted
                    and a root is specified, this will be the root that was
                    specified. Otherwise, it will be None. When the event is
                    dispatched, it will be spidered from this root rather than
                    emitted from the eventhub as usual.
    """
    
    __metaclass__ = EventM
    
    __slots__ = ['_subject', '_root']
    
    # This should be overridden by subclasses.
    signal = 'default'
    
    def __init__(self, subject, root=None):
        """ Initialize an Event object.
        
            Event.__init__(self, subject)
            Event.__init__(self, subject, root)
        
            self:       An instance of Event or one of its subclasses.
            
            subject:    An eventuser (instance of a subclass of
                        AbstractEventUser or AbstractEventUserBranch) that
                        caused the event to occur. None is not a valid type.
            
            root:       Indicates the root node from which the node should be
                        spidered, if spidering is used.
        """
        if subject is None:
            raise TypeError("`subject` may not be `None`!")
        self._subject = subject
        self._root = root
        try:
            self._sid = subject.id
        except AttributeError:
            self._sid = None
    
    _fget_subject = operator.attrgetter('_subject')
    _doc_subject =\
        """ The eventuser that caused the event.
            Read-only.
        """
    
    _fget_sid = operator.attrgetter('_sid')
    _doc_sid =\
        """ The ID of the event's subject.
            This will be None if the subject has no ID.
            Read-only.
        """
        
    _fget_root = operator.attrgetter('_root')
    _doc_root =\
        """ The root node of a spidered event.
            Should be None when the event is not a spidered event.
            Read-only.
        """
    
    def compare(self, desc):
        """ Compare the event to an event description.
        
            An "event description" is a dict or dict-compatible object that is
            compared to an event's data in some way. The `compare` method
            returns whether or not the description "matches" the event.
            
            The default `compare` method for events (ie. this one) simply tests
            for equality between the values of the given description and the
            attributes of an event. The description matches if and only if each
            value in the description is equal to the attribute of the event
            with a name that is equal to the key of the value in the
            description:
            
            >>> class MyEvent(Event):
            ...     __slots__ = ['a', 'b']
            ...
            >>> e = MyEvent(a=20, b=98)
            >>> e.compare({a : 20, b : 98})     # a and b are both equal
            True
            >>> e.compare({a : 21, b : 98})     # only b is equal
            False
            
            If the description contains a key for which there is no
            corresponding event attribute, a ValueError will be raised:
            
            >>> e.compare({c : 72})     # c is not an attribute of the event
            Traceback (most recent call last):
                ...
            ValueError: "c" is not a valid description key for this event type.
        
            The `compare` method can be overridden to allow for more complex
            comparisons and event descriptions when the default comparison
            fails with a ValueError:
            
            >>> class RangeEvent(Event):
            ...     __slots__ = ['a']
            ...     def compare(self, desc):
            ...         ''' Checks if self.a is in the range indicated by desc.
            ...             The range is specified by the keys `a_min` and
            ...             'a_max' in the description.
            ...         '''
            ...         # Try the default comparison _first_.
            ...         try:
            ...             ret1 = super(RangeEvent,self).compare(desc)
            ...             ret2 = True
            ...         except ValueError, e:
            ...             # The default comparison failed, which means that
            ...             # the desc may be trying to invoke a range check.
            ...             try:
            ...                 min = desc['a_min']
            ...                 max = desc['a_max']
            ...             except KeyError:
            ...                 # It seems the range check was not invoked, so
            ...                 # re-raise the original ValueError. (This lets
            ...                 # us raise the expected type of error with
            ...                 # an accurate but simple error message.)
            ...                 raise e 
            ...             else:
            ...                 # Return the results of the range check.
            ...                 ret2 = (self.a >= min and self.a <= max)
            ...         # Return True only if the default AND extended tests
            ...         # passed.
            ...         return ret1 and ret2
            ...                 
            >>> e = RangeEvent(a = 23)
            >>> e.compare({'a_min' : 10, 'a_max' : 80})
            True
            >>> e.compare({'a_min' : 90}) # Fails because 'a_max' is missing.
            Traceback:
                ...
            ValueError "a_min" is not a valid description key for this type of
            event.
            
            Of course, there are many different ways a comparison method can
            be written depending on exactly what sort of action an event is
            made to represent. The only requirement is that the method returns
            True if the description matches the event, False if it does not,
            and raises a ValueError if the description contains keys that the
            comparison method cannot handle.
            
            The `compare` method is called against every hook that can
            potentially respond to an event. However, the signal of the event
            should always be compared with the hook's signal directly. This
            should happen _before_ the `compare` method is called, and
            `compare` should not be caleld if the hook's signal does not match
            the event's. This makes it safe to give a hook an event description
            that contains keys that not every event type will have, because
            descriptions will only be compared against events that have the
            correct signal.
            
            # TODO: Find a simpler example!
        """
        for k, v in dict(desc):
            try:
                equal = self.attrs[k] == v
            except AttributeError:
                raise ValueError('"%s" is not a valid description key for '
                                 'this type of event.' % k)
            if not equal:
                return False
        return True

class EventUserM(AbstractNode.__metaclass__):
    """ Metaclass for AbstractEventUser.
    
        There are two features provided to AbstractEventUser via this
        metaclass:
        
        1.)
    
        It provies additional functionality to the special class attribute
        `__primary_parent`, which indicates the next step on the path from the
        eventuser to the main tree root.
        
        Before `NodeM` is activated, `EventUserM` will generate a method called
        `set_PARENT_internal`, where PARENT is the value of `__primary_parent`.
        The method is defined as follows:
        
            def set_PARENT_internal(self, p, unlinked=False):
                ''' Used to internally set the PARENT.
        
                    This method internally sets the value of the `PARENT` attribute,
                    unlinking from the previous PARENT if necessary, and uses
                    `unify_eventhub` to make sure that the correct eventhub
                    is being used afterwards.
                '''
                self._set_PARENT_internal(p, unlinked)
                self.unify_eventhub(p.eventhub)
        
        Because the method is generated _before_ `NodeM` processes the class,
        `NodeM` will see this method and will rename the `set_parent_internal`
        that it generates. This renamed method can then be called from the one
        that is generated by `EventUserM`.
        
        2.)
        
        It generates a frozen set of methods that have been marked as event
        handlers. These are methods that have an attribute called "signal" or
        are named with the prefix "on_", where the remaining portion of the
        name is the signal. The set can be accessed via the class attribute
        `handlers`.
        
        Each subclass of an eventuser class will get its own `handlers` frozen
        set. This set will be the union of the handlers of all parent classes
        (following the MRO) and handlers in the current subclass. 
        
    """
    
    def __init__(cls, name, bases, dict_):
        
        # We make a copy of this dict because we will be editting it later.
        # (I do this because I'm not sure if we're supposed to edit the dict
        # that gets passed to the metaclass.)
        dict_ = dict(dict_)
        
        #.1) Add functionality to the `__primary_parent` attribute.
        
        # Find the name of the primary parent, if any.
        try:
            primary_parent = dict_['_'+name+'__primary_parent']
        except KeyError:
            pass
        else:
            # This returns the `_set_PARENT_internal` method that will be
            # generated by other metaclasses later.
            self_get__set_parent_internal =\
                operator.attrgetter("_get_" + primary_parent + "_internal")
            
            # The meat and potatoes: This whole metaclass exists so that this
            # function block doesn't have to be copy-and-pasted all over sbak.
            def set_parent_internal(self, p, unlinked=False):
                ''' Used to internally set the %(PARENT)s.
        
                    This method internally sets the value of the `%(PARENT)s` attribute,
                    unlinking from the previous %(PARENT)s if necessary, and uses
                    `unify_eventhub` to make sure that the correct eventhub
                    is being used afterwards.
                '''
                self_get__set_parent_internal(self)(p, unlinked)
                self.unify_eventhub(p.eventhub)
            
            # Put the name of the primary parent into the newly-generated
            # method.
            set_parent_internal.__name__ = "set_"+primary_parent+"_internal"
            set_parent_internal.__doc__ %= {"PARENT" : primary_parent}
            
            # Put the function we just made into the dict. It will be processed
            # by the other metaclasses to turn it into a proper instance method
            dict_[set_parent_internal.__name__] = set_parent_internal
        
        # Continue the metaclass pre-processing chain
        super(EventUserM,cls).__init__(name, bases, dict_)
        
        #2.) Build the handlers set.
        
        # This dict will at first hold the handlers of the bases. Every time a
        # handler is found among the bases, it replaces the handler with the
        # same name within this dict. After all handlers have been found in the
        # bases, handlers from the current class are added, replacing methods
        # of the same name.
        handlerdict = {}
        
        # Find each parent class that has a handlers set, and get handler
        # methods from them.
        for base in bases:
            try:
                # Update the dict from earlier with methods of the base class
                # currently being processed. The update keys will be the
                # names of the methods, and the values will be the methods
                # themselves.
                handlerdict.update((m.__name__, m) for m in base.handlers)
            except AttributeError:
                # An AttributeError means that the base is not an eventuser
                # class, so it does not have handlers and can be ignored.
                pass
        
        # Find all methods in the current class that are marked as handlers.
        for mname, m in dict_.iteritems():
            try:
                # Check to see if the method has a signal.
                m.signal
            except AttributeError:
                # Method does not have a signal, but it may still be a handler.
                # If the name begins with "on_", give it a signal that is equal
                # to the rest of the name and add it to the handlers.
                pre, mid, signal = mname.partition("on_")
                if not pre and mid:
                    # Fill in the blanks as needed
                    m.signal = signal
                    if not hasattr(m, 'desc'):
                        m.desc = {}
                    if not hasattr(m, 'priority'):
                        print m
                        m.priority = None
                    handlerdict[signal] = m
            else:
                # If the method has a signal, it is a handler, and should be
                # added.
                handlerdict[mname] = m
        
        # Make an immutable copy of the set of all handlers for this class.
        handlers = frozenset(handlerdict.values())
        setattr(cls, 'handlers', handlers)
        
        print cls, "EventUserM"

class AbstractEventUser(AbstractNode):
    """ Abstract base for event user objects.
    
        This class establishes a subnode-to-branch relationship type between
        `eventuser` and `eventhub`, where the former is an AbstractEventUser
        object functioning as a subnode, and the latter, usually an `EventHub`
        object, functioning as the branch.
        
        AbstractEventUser can be subclassed to create node types for event
        processing trees. In order to do this, the subclass has to define the
        following special attributes:
        
        __branch_names = <dict>
        
            This dict indicates a subnode-branch relationship between instances
            of this class and instances of a branch class. In order for an
            AbstractEventUser subclass to be compatible with such a branch,
            the branch class must have an attribute called `__branching_name`
            that is a string that is equal to one of the values in
            `__branch_names`, and another attribute called `__subnode_name`,
            also a string, that is equal to the corresponding key of
            `__branch_names`. When `__branch_names` is defined correctly, a set
            of class attributes named after the keys will be generated
            automatically within the new subclass. (See `sbak.tree`.)
        
        __primary_parent = <string>
    
            This string is the type-name of the next node from the current
            point in the main tree to the root. (A "type-name" is a string that
            is used in autogenerating methods and properties needed for
            creating relationships between objects.)
        
        ~~~ Instance Attributes ~~~
        
        priority:       Priority number of eventuser among other eventusers
                        listening for a particular signal. Eventusers are
                        notified of events in order from lowest eventuser
                        priority value to highest. (Lower value = higher
                        priority.)
                        
                        This will be None at first. If the eventuser is added
                        to an eventhub and the priority is still None, the
                        eventuser will give the eventuser a priority that is
                        equal to the highest priority number the eventuser has
                        yet encountered + 1.
    """
    
    __metaclass__ = EventUserM
    
    __slots__ = ['_AbstractEventUser__init', '_sighooks', 'priority', '_hipri']
    
    # Metaclassing stuff; see `sbak.base.LinkableM` and `sbak.tree`.
    __branch_names = { 'eventhub' : 'eventuser' }
    
    # Used to prevent multiple initializations without the use of super()
    __init = False
    
    def __init__(self, priority = None):
        """ Initialize an object derived from `AbstractEventUser`
        
            AbstractEventUser.__init__(self)
            AbstractEventUser.__init__(self, priority)
            
            self:       An instance of an `AbstractEventUser` subclass.
            
            priority:   Optional priority. If not given or None, priority will
                        be determined when the eventuser is added to an
                        eventhub based on the highest priority value of other
                        eventusers in the hub.
        
            This method is only effective once so that it is safe to call
            multiple times (ie. when used in a multiple inheritence scheme
            without the use of the `super` function).
        """
        if not self.__init:
            # Nullifies multiple calls
            self.__init = True
            
            # Maps all registered signals to their handlers.
            self._sighooks = {}
            
            # Default highest known hook priority is 0
            # (Used for determining priority of hooks that don't have a
            # priority)
            self._hipri = 0
            
            # Priority, if given
            self.priority = priority
            
            # Hook all class-level handlers.
            for h in self.handlers:
                self.hook(h.__get__(self, self.__class__),
                          h.signal, h.priority, h.desc)
    
    def hook(self, handler, signal_or_type, priority=None, desc=(), **kwdesc):
        """ Creates a new hook with this eventuser.
        
            eventuser.hook(handler, signal_or_type) -> hook
            eventuser.hook(handler, signal_or_type, priority) -> hook
            eventuser.hook(handler, signal_or_type, priority, desc) -> hook
            eventuser.hook(handler, signal_or_type, priority,
                            key1=val1, ..., keyN=val) -> hook
            eventuser.hook(handler, signal_or_type,
                            key1=val1, ..., keyN=val) -> hook
        
            Returns the hook. To unhook it, call `hook.unhook()`.
            
            See the `Hook` class for more information about the parameters.
        """
        h = Hook(handler, signal_or_type, priority, desc, **kwdesc)
        self.add_hook(h)
        return h

    def add_hook(self, h):
        """ Add an existing `Hook` object to the eventuser.
        
            eventuser.add_hook(h) -> None
            
            A hook cannot be added if it is already in use by the eventuser.
            The will cause a `ValueError` to occur.
        """
        sig = h.signal
        try:
            sighooks = self._sighooks[sig]
        except KeyError:
            sighooks = self._sighooks[sig] = set()
        if h in sighooks:
            raise ValueError("Hook %r already in use by eventuser %r" %
                             (h, self))
        sighooks.add(h)
        h._set_eventuser_internal(self)
        
        if h.priority is None:
            h.priority = self._hipri = self._hipri + 1
        elif h.priority > self._hipri:
            self._hipri = h.priority
    
    def unhook_handler(self, handler, signal_or_type=None):
        """ Unhook all hooks using a particular handler.
        
            eventuser.unhook_handler(handler) -> hooks
            eventuser.unhook_handler(handler, signal_or_type) -> hooks
            
            handler:        A callable that is currently being used as an event
                            handler by the eventuser.
            
            signal_or_type: Optional. The signal or the corresponding type. If
                            None or not given, all event signals are used.
                        
            A ValueError will be raised if `event_type` has a signal that is
            not bound by any hooks.
            
            Returns a list of all hooks that were unhooked.
        """
        found = []
        sighooks = self._sighooks
        if signal_or_type is not None:
            try:
                signal = signal_or_type.signal
            except AttributeError:
                signal = signal_or_type
            try:
                hooks = set(sighooks[signal])
            except KeyError:
                raise ValueError("no handlers hooked to signal %r" % signal)
        else:
            hooks = set().union(sighooks.values())
        for h in hooks:
            if h.handler == handler:
                h.unhook()
                found.append(h)
        return found
    
    def unhook_type(self, signal_or_type):
        """ Unhook all hooks for a particular event type/signal.
        
            eventuser.unhook_type(signal_or_type) -> handlers_unhooked
            
            Unhooks all hooks hooked to a particular signal. Returns a list
            of all hooks that were unhooked. If no hooks were unhooked, an
            empty list is returned.
        """
        try:
            signal = signal_or_type.signal
        except AttributeError:
            signal = signal_or_types
        try:
            hooks = list(self._sighooks[signal])
        except KeyError:
            return []
        else:
            for h in hooks:
                h.unhook()
            return hooks
    
    def _unhook_internal(self, hook):
        # Used internally to notify the eventuser that a hook is trying to
        # unhook itself
        sighooks = self._sighooks
        signal = hook.signal
        sighooks[signal].discard(hook)
        # Delete the set for a signal once the set is empty.
        if not len(sighooks[signal]):
            del sighooks[signal]
    
    def notify(self, e):
        """ Notify the event user that an event occured.
        
            This simply calls `trigger` with the event given, but can be
            overridden to do something else.
            
            For `AbstractEventUser`, the `notify` method is aliased as
            `forward_spider`, since spidering to a terminal eventuser node is
            nothing more than an ordinary notification. Subclasses of
            `AbstractEventUser` do not have to define `forward_spider` as an
            alias to `notify`.
            
            Returns the value returned by `trigger`, if any.
        """
        return self.trigger(e)
    
    _getpri = operator.attrgetter('priority')
    
    def trigger(self, e):
        """ Trigger all hooks of the eventuser matching the given event.
        
            In order for a hook to match an event, one of the event's signals
            must be equal to the hook's signal, and `e.compare(hook.desc)` must
            return True for the hook.
            
            Hooks are triggered in order from lowset `priority` value to
            highest.
        """
        _sighooks = self._sighooks
        sighooks = []
        extend = sighooks.extend
        for sig in e.signals:
            try:
                extend(_sighooks[sig])
            except KeyError:
                pass
        [h(e) for h in sorted(sighooks, key=self._getpri) if e.compare(h.desc)]
    
    def unify_eventhub(self, hub):
        """ Used to make sure all related eventusers share the same eventhub.
        
            hub:    The eventhub that this and all related eventusers should be
                    sharing.
                    
            This method is used in the implementation of event processing
            trees. It is part of a mechanism that is designed to prevent
            different eventuser nodes in an event processing tree from having
            different event hubs. It is also designed to aid the process of
            automatically assigning the correct eventhub to each eventuser.
            
            Whenever this is called, a series of conditions are tested. The
            results of these tests determine what happens in regards to
            the current eventuser's eventhub:
            
            1.) If `hub` is None, then this eventuser's eventhub is set to
                None.
            
            2.) If the current eventuser has no eventhub and `hub` is not None,
                then this eventuser's eventhub is set to `hub`.
                
            3.) If the current eventiser does have an eventhub and `hub` is not
                None, then `sbak.error.Error` is raised,
            
            4.) Under any other circumstances (ie. both eventhubs are the same,
                both eventhubs are None, etc.), then nothing happens.
                
            The `unify_eventhub` method should be called whenever the
            current eventuser is added to an event processing tree. The
            `AbstractEventUser` class does not call this method itself; it
            relies on subclasses to call the method at the appropriate time.
            
            If the eventuser is going to be used as a branch (formal or
            informal--see `sbak.tree`) then this method should be overloaded
            so that it is called not only on the current node but also on each
            of the current node's subnodes. For the subnodes, the argument to
            `unify_eventhub` should be the current node's eventhub after
            `super(cls,self).unify_eventhub(hub)` is called.
            
            All of ScrollBack's eventuser classes will call this at the
            apropriate time as long as they are used correctly as indicated by
            each class's documentation. You should not need to worry about
            calling this method yourself unless you are implmeneting a new
            eventuser class that does not fit in with the model of any of
            ScrollBack's standard event processing trees.
        """
        
        # If `hub` is None, then this eventuser's eventhub is set to None.
        if hub is None:
            self.eventhub = None
        
        # If the current eventuser has no eventhub and `hub` is not None, then
        # this eventuser's eventhub is set to `hub`.
        elif self.eventhub is None and hub is not None:
            self.eventhub = hub
        
        # If the current eventuser does have an eventhub and `hub` is not None,
        # then `sbak.error.Error` is raised,
        elif self.eventhub is not hub:
            raise sbak.error.Error(
                "eventhub conflict; %r.eventhub is %r, tried to set to %r" %
                (self, self.eventhub, hub)
            )
     
    def _fget_signals(self,
            # Experimental optimization.
            _getsighooks = operator.attrgetter('_sighooks'),
            _getkeys = operator.attrgetter('keys')):
        """ Tuple of all signals that the eventuser is currently listening for.
            Read-only.
        """
        return _getkeys(_getsighooks(self))()
        
    def emit(self, event_type, *vadesc, **kwdesc):
        """ Emit an event from the eventuser.
        
            eventuser.emit(event_type, ...) -> e
            
            event_type:    A valid event type, or at least an event factory.
            
            ...:            Additional parameters are passed to `event_type`
                            after the first two arguments (the eventuser and
                            a root of None).
            
            This will create a new event using `event_type` with a subject of
            the current evnetuser and a root of `None`. Additional arguments to
            `emit` are passed to `event_type` as *vargs and **kwargs
            arguments. These occur after the `subject` and `root` arguments.
            The event created will be immediatly sent to the current
            eventuser's eventhub via `eventuser.eventhub.emit`.
            
            Example:
            
            >>> class MyEvent(Event):
            ...     signal = "myevent"
            ...     def __init__(self, subject, root=None, foo=None):
            ...         self.foo = foo
            ...
            >>> e = myeventuser.emit(MyEvent, 99999)
            >>>
            >>> # ...Hooks for "myevent" were just triggered...
            >>>
            >>> # MyEvent got the extra data passed to emit
            >>> e.foo
            99999
        """
        e = event_type(self, None, *vadesc, **kwdesc)
        self.eventhub.emit(e)
        return e
    
    def post(self, event_type, *vadesc, **kwdesc):
        """ Post an event from the eventuser.
        
            eventuser.post(event_type, ...) -> e
            
            This method works the same way as `AbstractEventUser.emit`, except
            that the event is posted to the queue of the eventuser's eventhub
            using `eventuser.eventhub.post`. When the events of the queue are
            later dispatched, its as if all queued events are emitted
            consecutively in the order they were queued.
        """
        e = event_type(self, None, *vadesc, **kwdesc)
        self.eventhub.post(e)
        return e
    
    def spider(self, event_type, root=None, *vadesc, **kwdesc):
        """ Spider an event from the evnetuser via another node.
        
            eventuser.spider(event_type, root, ...) -> e
            eventuser.spider(event_type, key1=val1, ..., keyN=valN) -> e
            
            event_type:    A valid event type, or at least an event factory.
            
            root:           A node from which the event will be spidered. If
                            None, `eventuser.get_root()` will be used.
            
            ...:            Variable arguments after `root` and keyword
                            arguments are passed to `event_type`.
            
            key1=val1, ..., keyN=valN:
                            
                            If `root` is ommitted, additional arguments to
                            `event_type` must be specified using keyword
                            arguments.
            
            This will create a new event with the current eventuser as the
            subject and the root specified. Additional arguments are passed to
            the event type given. After the event is created, the event will
            start spidering from `root` using `root.forward_spider`.
            
            Example:
            
            >>> class MyEvent(Event):
            ...     signal = "myevent"
            ...     def __init__(self, subject, root=None, foo=None):
            ...         self.foo = foo
            ...
            >>> e = myeventuser.spider(MyEvent, foo = 99999)
            >>>
            >>> # ...e is now spidering through the event processing tree...
            >>>
            >>> # MyEvent got the extra data passed to emit
            >>> e.foo
            99999
        
        """
        if root is None:
            root = self.get_root()
        e = event_type(self, root, *vadesc, **kwdesc)
        root.forward_spider(e)
        return e
    
    def spiderpost(self, event_type, root=None, *vadesc, **kwdesc):
        """ Spiderpost an event from the current eventuser.
        
            eventuser.spiderpost(event_type, root, ...) -> e
            eventuser.spiderpost(event_type, key1=val1, ..., keyN=valN) -> e
            
            This is a functional combination of `AbstractEventUser.spider` and
            `AbstractEventUser.post`. An event is created and posted to the
            queue of the eventuser's eventhub, but when the queue is later
            dispatched, it is spidered from the given root node rather than
            emitted.
        """
        if root is None:
            root = self.get_root()
        e = event_type(self, root, *vadesc, **kwdesc)
        self.eventhub.post(e)
        return e

    # For the basic eventhub, forwarding a spider is nothing more than
    # notification.
    forward_spider = notify
    
    def get_hooks(self, event_type=None):
        """ Returns a list of all hooks for the eventuser.
        
            eventuser.get_hooks() -> hooks
            eventuser.get_hooks(event_type) -> hooks
        
            If an event_type is specified, only hooks that have a signal that
            is one of the class's signals will be returned.
            
            Hooks are returned in order from lowest to highest priority.
            
            If there are no hooks for the given event_type, an empty list
            is returned.
        """
        if event_type is not None:
            hooks = []
            for sig in event_type.signals:
                try:
                    hooks.extend(_sighooks[sig])
                except KeyError:
                    pass
            hooks.sort(key=self._getpri)
        else:
            hooks = sorted(
                set().union(*self._sighooks.values()),
                key = self._getpri
            )
        
        return hooks

class EventUserBranchM(AbstractBranch.__metaclass__,
                       AbstractEventUser.__metaclass__):
    def __init__(cls, name, bases, dict):
        super(EventUserBranchM,cls).__init__(name,bases,dict)

class AbstractEventUserBranch(AbstractBranch, AbstractEventUser):
    """ An eventuser that also functions as a branch for more eventusers.
    
        There are three major differences from AbstractEventUser:
        
        1.) Ability to have subnodes of a particular type.
        
        2.) Changing the eventhub will also change the eventhub of all
            subnodes,
        
        3.) Spidering is forwarded to each child.
        
        This class is "abstract", meaning that it will not function correctly
        except as a superclass to a class that defines certain methods or other
        attributes. It must be subclassed, and the subclass must define the
        following:
        
        __branching_name = <string>
        
            The type-name of a branch from the perspective of subnodes of the
            branch.
        
        __subnode_name = <string>
        
            The type-name of a subnode from the perspective of the subnode's
            branch.
        
        (NOTE: The special attributes of AbstractEventUser subclasses are also
        needed if an AbstractEventUserBranch is going to function as an
        eventuser as well.)
        
        If the above attributes are defined, then the AbstractEventUserBranch
        subclass will be given the methods and properties it needs to become a
        valid eventuser branch class automatically.
        
        See `sbak.event.EventUserBranchM` and `sbak.tree` for more information
        about the special class attributes and automatically-generated methods
        and properties.
    """
    
    __metaclass__ = EventUserBranchM
    
    # Used to prevent multiple initializations without the use of super()
    __init = False
    
    def __init__(self, children=None):
        """ Initialize the basic components of `AbstractEventUserBranch`.
        
            AbstractEventUserBranch.__init__(self)
            AbstractEventUserBranch.__init__(self, children)
            
            self:       An object derived from `AbstractEventUserBranch`
            children:   Optional sequence of objects derived from
                        `AbstractEventUser`. These will be added to the
                        branch immediately.
                        
            This method will only be effective once, so it is safe to call
            multiple times (ie. when used in a multiple inheritence scheme).
        """
        if not self.__init:
            self.__init = True
            AbstractEventUser.__init__(self)
            AbstractBranch.__init__(self, children)
    
    def unify_eventhub(self, hub):
        """ Used to make sure this and all child eventusers share an eventhub.
    
            hub:    The eventhub that this eventuser and all child eventusers
                    should have. Pass None to indicate that the current
                    eventuser and all children will no longer be a part of an
                    event processing tree and will therefore have no hub.
            
            Note: Read `sbak.event.AbstractEventUser.unify_eventhub` before
            reading below.
            
            This version of the `unify_eventhub` method expands on
            the `AbstractEventUser` class's version by calling
            `unify_eventhub` on each subnode of the current eventuser
            branch with the current eventuser's eventhub as the argument.
            This makes sure that the subnodes' eventhubs are updated correctly.
        """
        super(AbstractEventUserBranch,self).unify_eventhub(hub)
        for c in self:
            c.unify_eventhub(self.eventhub)
    
    def forward_spider(self, e):
        """ Notify this eventuser branch and each child of an event.
        
            This will call `notify` on self, and will call `forward_spider` for
            each child of self.
        """
        self.notify(e)
        for child in self:
            child.forward_spider(e)

class EventHub(AbstractBranch):
    """ Central communication hub of eventusers in an event processing system.
    
        TODO: Document me!
    """
    
    __slots__ = ['_queue', '_sigusers', '_hipri']
    
    __branching_name = 'eventhub'
    __subnode_name = 'eventuser'
    
    def __init__(self, eventusers=None):
        
        # Internal queue of posted events. Holds an unlimited number of events.
        self._queue = Queue.Queue(0)
        
        # Maps signals to sets that contain eventusers that are listening for
        # them.
        self._sigusers = {}
        
        # Highest priority ever encountered by the eventhub. Starts at 0 and
        # will grow as eventusers are added.
        self._hipri = 0
        
        AbstractBranch.__init__(self, eventusers)
        
    def append_subnode_internal(self, subnode, index=None):
        """ Used to internally append or insert a subnode.
        
            This relies on the auto-generated method
            `_append_subnode_internal`, which is what actually appends the
            subnode to the eventhub.
            
            You don't normally want to call this directly.
        """
        self._append_subnode_internal(subnode, index)
        for sig in subnode.signals:
            try:
                sigusers = self._sigusers[sig]
            except KeyError:
                sigusers = self._sigusers[sig] = set()
            sigusers.add(subnode)
        if subnode.priority is None:
            subnode.priority = self._hipri = self._hipri + 1
        elif subnode.priority > self._hipri:
            self._hipri = subnode.priority
    
    _getpri = operator.attrgetter('priority')
    
    def emit(self, e):
        """ Emit an event to all observing eventusers.
        
            eventhub.emit(e)
            
            e:      An Event object to emit.
        
            As soon as an event is emitted to an eventhub, the eventhub
            notifies each eventuser that is observing for an event that has
            the emitted event's signal.
        """
        try:
            eventusers = sorted(self._sigusers[e.signal], key=self._getpri)
        except KeyError:
            pass
        else:
            for eventuser in eventusers:
                eventuser.notify(e)
    
    def post(self, e, root=None):
        """ Post an event to the queue.
        
            eventhub.post(e)
        
            e:      An Event object to post.
        
            When an event is posted, observing eventusers are not immediately
            notified of the event. Instead, the event is merely placed on a
            queue and is later "dispatched" from the queue to observers.
        """
        self._queue.put_nowait(e)
    
    _getroot = operator.attrgetter('root')
    _getforward = operator.attrgetter('forward_spider')
    
    def dispatch(self):
        """ Dispatch all events on the eventhub's queue.
        
            eventhub.dispatch() -> None
        
            Each event in the queue will be either spidered or emitted
            depending on whether or not the event's root is None. If it is
            None, the event is emitted. If it is not None, the event is
            spidered from the root using `forward_spider`.
            
            Dispatching works by repeatedly popping events from the queue.
            The loop does not end until the queue is completely empty, so it
            will keep going as long as event handlers keep posting more events.
        """
        # Optimize access to some frequently-needed names
        get = self._queue.get
        emit = self.emit
        getroot = self._getroot
        getforward = self._getforward
        
        # Keep looping until the queue is empty
        try:
            while True:
                e = get()
                if getroot(e) is None:
                    emit(e)
                else:
                    getforward(getroot(e))(e)
        except Queue.Empty:
            pass
    
class Hook(AbstractNode):
    """ A binding between an event handler and an event description.
    
        ~~~ Class Usage ~~~
    
        Hook(handler, signal_or_type) -> hook_obj
        Hook(handler, signal_or_type, priority) -> hook_obj
        Hook(handler, signal_or_type, priority, desc) -> hook_obj
        Hook(handler, signal_or_type, priority,
            desckey1 = descval1, desckey2 = descval2, ...) -> hook_obj
        Hook(handler, signal_or_type,
            desckey1 = descval1, desckey2 = descval2, ...) -> hook_obj
        
        handler:    An event handler callable that the eventuser would like to
                    be called when the hook is triggered.
                    
        signal_or_type:
                    The signal of the event being listened for, or an Event
                    type class with the signal being listened for.
                    
        priority:   The priority of the Hook. Determines the order in which
                    hooks are triggered if multiple handlers of an eventuser
                    are hooked to the same signal. If not given or None, a
                    priority will be determined at the time the hook is added
                    to the eventuser.
        
        desc:       Optional event description. This is a dict that will be
                    passed to the event's `compare` method when an event's
                    signal matches the hook's. The hook is only triggered when
                    the event's `compare` method returns True.
        
        desckey1,   Event descriptions can also be specified using keyword
        desckey2,   arguments.
        ...
        
        ~~~ Description ~~~
        
        When an eventuser starts listening for a particular event, it binds a
        callable (usually one of the eventuser's own methods) to an event
        description. The callable is thereafter refered to as a "handler",
        and the binding between it an the event description is called a "hook."
        A hook is represented by an instance of the `Hook` class.
        
        `Hook` instances are callable. Calling a hook is called "triggering".
        The hook expects a single argument called `e`. This is presumably the
        `Event` object that matched the hook's event description and caused the
        hook to be triggered.
        
        When called, the hook object will call its event handler with the
        following convention:
        
        ehandler(hook, e)
        
        ehandler:   The hook's event handler.
        
        hook:       The hook that is calling the event handler.
        
        e:          The event that caused the hook to call its event handler.
        
        ~~~ Attributes ~~~
        
        These attributes should be available for all hooks.
        
        eventuser:  The observer that "created" the hook. Currently read-only.
        
        handler:    The callable that gets called when the hook is triggered.
        
        signal:     The event signal that triggers the hook.
        
        desc:       Additional event description data that must match an event
                    before the hook can be triggered. A hook is only triggered
                    if this matches an event. If there is no additional
                    description data, then `desc` will be an empty dict.
        
        priority:   Priority number of hook among other hooks that are
                    triggered by the same signal. When an eventuser's hooks are
                    triggered for a particular event signal, the hooks are
                    triggered in order from lowest to highest priority number.
                    (Lower value = higher priority.)
                    
                    When a hook is first created its priority defaults to None.
                    If the priority remains None by the time the Hook is added
                    to an eventuser then the priority will be given the highest
                    known hook priority value + 1.
    """
    
    __slots__ = ['_eventuser', 'handler', 'signal', 'desc', 'priority']
    
    __branch_names = {"hooks" : "hook"}
    
    def __init__(self, handler, signal_or_type, priority=None, desc=(), **kwdesc):
        """ Initialize a Hook object.
        
            Hook.__init__(self, handler, signal)
            Hook.__init__(self, handler, signal, priority)
            Hook.__init__(self, handler, signal, priority, desc)
            Hook.__init__(self, handler, signal, priority,
                key1=val1, ..., keyN=valN)
            Hook.__init__(self, handler, signal,
                key1=val1, ..., keyN=valN)
        """
        self._eventuser = None
        self.priority = priority
        
        # Store the passed data
        self.handler = handler
        try:
            self.signal = signal_or_type.signal
        except AttributeError:
            self.signal = signal_or_type
        
        # Merge the desc and kwdesc arguments
        desc = dict(desc)
        desc.update(kwdesc)
        self.desc = desc
    
    def __call__(self, e):
        """ Allows the hook to be called.

            hook.__call__(e) <==> hook(e) <==> hook.handler(hook, e)
        
            When a hook is called, it calls the hook's event handler. The
            handler is passed two arguments: the hook, and the event for which
            the hook was triggered (called).
            
            The return value of the handler is retuned by the hook.
        """
        return self.handler(self, e)
    
    _fget_eventuser = operator.attrgetter("_eventuser")
    _doc_eventuser =\
        """ The eventuser that is currently using this hook.
            Read-only. To make the eventuser stop using the hook, use `unhook`.
        """
    
    def _set_eventuser_internal(self, eventuser):
        # Used to set the eventuser internally.
        # Should not be called by user code.
        self._eventuser = eventuser
    
    def unhook(self):
        """ Removes the hook from its eventuser.
            This will change the value of `eventhook` to None.
            Fails with AttributeError if called multiple times.
        """
        self.eventuser._unhook_internal(self)
        self._set_eventuser_internal(None)